package com.ruoyi.web.controller.dd;


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiCallBackDeleteCallBackRequest;
import com.dingtalk.api.request.OapiCallBackRegisterCallBackRequest;
import com.dingtalk.api.response.OapiCallBackRegisterCallBackResponse;
import com.dingtalk.api.response.OapiProcessinstanceGetResponse;
import com.dingtalk.api.response.OapiV2UserGetResponse;
import com.dingtalk.oapi.lib.aes.DingTalkEncryptor;
import com.dingtalk.oapi.lib.aes.Utils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.config.DDConstant;
import com.ruoyi.common.config.DDUrlConstant;
import com.ruoyi.common.constant.CrmConstant;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.service.ProcessService;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.dd.AccessTokenUtil;
import com.ruoyi.common.utils.dd.DdConfigSign;

import com.ruoyi.crm.domain.CrmContract;
import com.ruoyi.crm.domain.CrmCost;
import com.ruoyi.crm.service.ICrmCostService;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.C;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.*;

import static com.ruoyi.common.config.DDConstant.TRAVEL_CODE;

/**
 * 审批回调信息处理
 */
@RestController
@Slf4j
public class CallbackController {
    @Autowired
    private ProcessService processService;

    @Autowired
    private ICrmCostService crmCostService;

    @Autowired
    private ISysDeptService sysDeptService;

    @Autowired
    private ISysUserService userService;

    private static final Logger bizLogger = LoggerFactory.getLogger("BIZ_CALLBACKCONTROLLER");
    private static final Logger mainLogger = LoggerFactory.getLogger(CallbackController.class);

    /**
     * 创建套件后，验证回调URL创建有效事件（第一次保存回调URL之前）
     */
    private static final String CHECK_URL = "check_url";

    /**
     * 审批任务回调
     */
    private static final String BPMS_TASK_CHANGE = "bpms_task_change";

    /**
     * 审批实例回调
     */
    private static final String BPMS_INSTANCE_CHANGE = "bpms_instance_change";

    /**
     * 相应钉钉回调时的值
     */
    private static final String CALLBACK_RESPONSE_SUCCESS = "success";


    @RequestMapping(value = "/callback", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, String> callback(@RequestParam(value = "signature", required = false) String signature,
                                        @RequestParam(value = "timestamp", required = false) String timestamp,
                                        @RequestParam(value = "nonce", required = false) String nonce,
                                        @RequestBody(required = false) JSONObject json) {
        String params = " signature:" + signature + " timestamp:" + timestamp + " nonce:" + nonce + " json:" + json;
        System.out.println("params: " + params);
        try {
            DingTalkEncryptor dingTalkEncryptor = new DingTalkEncryptor(DDConstant.TOKEN, DDConstant.ENCODING_AES_KEY,
                    DDConstant.APP_KEY);
            //从post请求的body中获取回调信息的加密数据进行解密处理
            String encryptMsg = json.getString("encrypt");
            String plainText = dingTalkEncryptor.getDecryptMsg(signature, timestamp, nonce, encryptMsg);
            JSONObject obj = JSON.parseObject(plainText);
            //根据回调数据类型做不同的业务处理
            String eventType = obj.getString("EventType");
            if (BPMS_TASK_CHANGE.equals(eventType)) {
                bizLogger.info("收到审批任务进度更新: " + plainText);
                //实现审批的业务逻辑，如发消息
            } else if (BPMS_INSTANCE_CHANGE.equals(eventType)) {
                //只抓取审批通过的 差旅费
                if (!(obj.containsKey("result") && obj.getString("result").equals("agree"))) {
                    bizLogger.info("不同意审批流程: " + obj);
                    return dingTalkEncryptor.getEncryptedMap(CALLBACK_RESPONSE_SUCCESS, System.currentTimeMillis(), Utils.getRandomStr(8));
                }
                bizLogger.info("收到审批实例状态更新: " + plainText);
                // 实现审批的业务逻辑
                String processInstanceId = obj.getString("processInstanceId");
                String processCode = obj.getString("processCode");
                //只获取差旅费流程
                if (StringUtils.isNotEmpty(processCode) && !processCode.equals(TRAVEL_CODE)) {
                    return dingTalkEncryptor.getEncryptedMap(CALLBACK_RESPONSE_SUCCESS, System.currentTimeMillis(), Utils.getRandomStr(8));
                }
                //根据流程id 获取流程实例
                OapiProcessinstanceGetResponse.ProcessInstanceTopVo processinstance = processService.getProcessinstanceById(processInstanceId);

                String originatorDeptId = processinstance.getOriginatorDeptId();

                SysDept dept = sysDeptService.selectDeptById(Long.parseLong(originatorDeptId));

                String ancestors = dept.getAncestors();

                // 只获取数字事业部的数据
                if (!ancestors.contains(CrmConstant.NUMBER_DEPT_ID.toString())){
                    return dingTalkEncryptor.getEncryptedMap(CALLBACK_RESPONSE_SUCCESS, System.currentTimeMillis(), Utils.getRandomStr(8));
                }


                //根据钉钉用户id 获取用户详情
                SysUser userInfo = userService.selectUserByTenantId(processinstance.getOriginatorUserid());
                List<OapiProcessinstanceGetResponse.FormComponentValueVo> formComponentValues = processinstance.getFormComponentValues();

                bizLogger.info("--------------------组件信息开始-------------------------");
                Map<String, String> map = new HashMap<>();
                for (OapiProcessinstanceGetResponse.FormComponentValueVo fo : formComponentValues) {
                    bizLogger.info("组件id: " + fo.getId());
                    bizLogger.info("组件名称: " + fo.getName());
                    bizLogger.info("标签值: " + fo.getValue());
                    bizLogger.info("标签扩展值: " + fo.getExtValue());
                    bizLogger.info("组件类型: " + fo.getComponentType());
                    ObjectMapper mapper = new ObjectMapper();

                    JsonNode rootNode = mapper.readTree(fo.getExtValue());

                    for (JsonNode node : rootNode) {
                        String name = node.get("name").asText();
                        String value = node.get("value").asText();

                        if ("总金额".equals(name)) {
                            map.put("总金额", value);
                        } else if ("出差事由".equals(name)) {
                            map.put("出差事由", value);
                        }
                    }
                }
                CrmCost crmCost = new CrmCost();
                //部门id 默认取第一个
                crmCost.setDeptId(userInfo.getDeptId());
                //用户名称
                crmCost.setUserName(userInfo.getUserName());
                //用户id
                crmCost.setUserId(userInfo.getUserId());
                //出差事由
                crmCost.setBusinessTrip(map.get("出差事由"));
                //总金额
                crmCost.setGrossAmount(new BigDecimal(map.get("总金额")));
                crmCost.setCreateTime(DateUtils.getNowDate());

                //保存 差旅费用实例
                crmCostService.save(crmCost);
            } else {
                // 其他类型事件处理
                bizLogger.info("其他流程: " + obj);
            }

            // 返回success的加密信息表示回调处理成功
            return dingTalkEncryptor.getEncryptedMap(CALLBACK_RESPONSE_SUCCESS, System.currentTimeMillis(), Utils.getRandomStr(8));
        } catch (Exception e) {
            //失败的情况，应用的开发者应该通过告警感知，并干预修复
            mainLogger.error("回调错误" + params, e);
            return null;
        }
    }

    /**
     * 注册回调
     *
     * @return
     * @throws Exception
     */
    @RequestMapping("/registForDD")
    public String regist() throws Exception {
        // 先删除企业已有的回调
        DingTalkClient client = new DefaultDingTalkClient(DDUrlConstant.DELETE_CALLBACK);
        OapiCallBackDeleteCallBackRequest request = new OapiCallBackDeleteCallBackRequest();
        request.setHttpMethod("GET");
        client.execute(request, AccessTokenUtil.getAccessToken());

        // 重新为企业注册回调
        client = new DefaultDingTalkClient(DDUrlConstant.REGISTER_CALLBACK);
        OapiCallBackRegisterCallBackRequest registerRequest = new OapiCallBackRegisterCallBackRequest();
        registerRequest.setUrl(DDConstant.CALLBACK_URL_HOST + "/callback");
        registerRequest.setAesKey(DDConstant.ENCODING_AES_KEY);
        registerRequest.setToken(DDConstant.TOKEN);
        registerRequest.setCallBackTag(Arrays.asList("bpms_instance_change", "bpms_task_change"));
        OapiCallBackRegisterCallBackResponse registerResponse = client.execute(registerRequest, AccessTokenUtil.getAccessToken());
        if (registerResponse.isSuccess()) {
            System.out.println("回调注册成功了！！！");
            return "success";
        }
        return "false";
    }

    /**
     * 获取钉钉 JSAPI 签名
     *
     * @return
     */
    @RequestMapping("/getSign")
    @ResponseBody
    public AjaxResult getSign(@RequestParam(value = "path") String path) throws Exception {
        String token = DdConfigSign.getJsapiTicket();
        Long time = System.currentTimeMillis();
        String randomStr = DdConfigSign.getRandomStr(6);
        String sign = DdConfigSign.sign(token, randomStr, time, path);
        Map<String, String> map = new HashMap<>();
        map.put("time", time.toString());
        map.put("randomStr", randomStr);
        map.put("signStr", sign);
        return AjaxResult.success(map);
    }
}
